#ifndef __SUBSYSTEM_CREATURE_SPAWN_H__
#define __SUBSYSTEM_CREATURE_SPAWN_H__

#include "AStar/AStarVector3.h"
#include "Subsystems/Subsystem.hpp"
#include <string>
#include <functional>
#include "Engine/Vector2.hpp"
#include "concurrentqueue.h"
#include "GameType.h"

namespace PocketSurvival
{
    enum class SpawnLocationType
    {
        Surface     ,       // 表面
        Cave        ,       // 洞穴
        Water               // 水中
    };

    struct CreatureType
    {
        std::string entityName;
        SpawnLocationType spawnLocationType;
        bool randomSpawn;
        bool constantSpawn;

        std::function<float(CreatureType*,const Point3&)> spawnSuitabilityFunction;
        std::function<int32_t(CreatureType*,const Point3&)> spawnFunction;

        inline CreatureType(const std::string &name, 
            SpawnLocationType theLocationType, 
            bool theRandomSpawn, 
            bool theConstantSpawn) :
                entityName(name),
                spawnLocationType(theLocationType),
                randomSpawn(theRandomSpawn),
                constantSpawn(theConstantSpawn) {}

        inline CreatureType(const std::string &name, 
            SpawnLocationType theLocationType, 
            bool theRandomSpawn, 
            bool theConstantSpawn, 
            std::function<float(CreatureType*,const Point3&)> fun1, 
            std::function<int32_t(CreatureType*,const Point3&)> fun2) :
                entityName(name),
                spawnLocationType(theLocationType),
                randomSpawn(theRandomSpawn),
                constantSpawn(theConstantSpawn),
                spawnSuitabilityFunction(fun1),
                spawnFunction(fun2) {}

    };

    class SubsystemGameInfo;
    class SubsystemTerrain;
    class SubsystemTimeOfDay;
    class SubsystemWeather;
    class SubsystemBodies;
    class SubsystemAnimalEntities;
    class SubsystemPlayers;

    struct ComponentBody;

    class SubsystemCreatureSpawn : public Subsystem
    {
    protected:
        SubsystemGameInfo *m_gameInfo;
        SubsystemTerrain *m_terrain;
        SubsystemTimeOfDay *m_timeOfDay;
        SubsystemWeather *m_weather;
        SubsystemBodies *m_bodies;
        SubsystemAnimalEntities *m_animalEntities;
        SubsystemPlayers *m_players;

        std::vector<CreatureType*> m_creatureTypeVec;
        std::vector<ComponentBody*> m_componentBodies;
        std::vector<PlayerDataPtr> m_playerVec;

        moodycamel::ConcurrentQueue<Point2> m_loadChunkQueue;
        std::set<Point2> m_loadedChunk;

        const int32_t m_totalLimit = 84;
        const int32_t m_totalLimitConstant = 56;
        const int32_t m_areaLimit = 9;
        const int32_t m_areaLimitConstant = 12;
        const int32_t m_areaRadius = 16;
        const int32_t m_areaRadiusConstant = 42;
        const int32_t m_maxPlayerAreaLimit = 36;
        const int32_t m_maxPointLimit = 3;

    public:

        void initializeCreatureTypes();
        int32_t spawnCreatures(CreatureType *creatureType, const std::string& templateName, const Point3& point, int32_t count);
        std::shared_ptr<Point3> processSpawnPoint(const Point3& spawnPoint, SpawnLocationType spawnLocationType);
        bool testSpawnPoint(const Point3& spawnPoint, SpawnLocationType spawnLocationType);
        void spawnCreaturesOnChunk(const Point2& coords, int32_t maxAttempts, bool constantSpawn);
        void spawnRandomCreature();
        
        std::shared_ptr<Point3> getRandomSpawn(const Vector3& position, SpawnLocationType spawnLocationType);
        std::shared_ptr<Point3> getRandomChunkSpawnPoint(const Point2& coords, SpawnLocationType spawnLocationType);

        int32_t getRandomWeightedItem(const std::vector<float> &items);



        int32_t countCreature(bool constantSpawn);
        int32_t countCreatureInArea(const Vector2& c1, const Vector2& c2, bool constantSpawn);


        virtual void onTerrainChunkAdded(TerrainChunk *terrainChunk) override;

        virtual void update(float dt) override;
        virtual void load(const nlohmann::json &json) override;

        virtual const std::string &getName() const override;
        virtual SubUpdateType getUpdateType() override;
    };

} // namespace PocketSurvival

#endif
